/**
 * ImageCache.java  [V 1.0.0]
 * classpath : com.zyw.utils.ImageCache
 * zhangyw Create at 2014年8月11日 下午3:46:34
 */
package com.zyw.utils;

import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.v4.util.LruCache;
import android.util.Log;

/**
 * 这里我们使用单例模式来做,导致的缺陷是，在应用最开始创建时，要设置ImageCache类的Context！！
 * 
 * @author zhangyw create at 2014年8月11日 下午3:46:34
 */
public class ImageCache {
  private static final String TAG = "ImageCache";
  private LruCache<String, Bitmap> mMemcache;
  private SimpleDiskCache mDiskCache;
  public static final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  private static Context mContext;
  /**
   * Use 1/8th of the available memory for this memory cache.
   */
  public static final int MEM_CACHE_SIZE = maxMemory / 8;
  public static final int DISK_CACHE_SIZE = 256;  //MEM_CACHE_SIZE;

  private ImageCache() {
    initMemCache();
    initDiskCache();
  }

  private static class InstanceHolder {
    private static ImageCache mImageCache = new ImageCache();
  }

  public static ImageCache getInstance() {
    return InstanceHolder.mImageCache;
  }

  private void initMemCache() {
    // 因为我们给出的是一个内存的大小，所以，我们必须将sizeof重写为数据的大小，其该值默认为cache中实体的个数

    mMemcache = new LruCache<String, Bitmap>(MEM_CACHE_SIZE) {
      /*
       * (non-Javadoc)
       * 
       * @see android.util.LruCache#sizeOf(java.lang.Object, java.lang.Object)
       */
      @SuppressLint("NewApi")
      @Override
      protected int sizeOf(String key, Bitmap value) {
        int rst = 0;
        if(VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB_MR1){
          rst=(int) (value.getByteCount() / 1024);
        }else{
          rst=value.getRowBytes()/1024;
        }
        return rst == 0 ? 1 : rst;
      }
    };
  }

  private void initDiskCache() {
    File dir;
    if (mContext == null||null==(dir=getDiskCacheDir(mContext, null))) {
      Log.w(TAG, "fail to init the DiskCache");
      return;
    }
    mDiskCache = new SimpleDiskCache(dir, DISK_CACHE_SIZE);
  }

  /**
   * @return the mContext
   */
  public static Context getContext() {
    return mContext;
  }

  /**
   * @param mContext
   *          the mContext to set
   */
  public static void setContext(Context mContext) {
    ImageCache.mContext = mContext;
  }

  public void addBitmap2Cache(String bitmapKey, Bitmap bitmap) {
    if (null != bitmapKey && null != bitmap) {
      addBitmap2MemCache(bitmapKey, bitmap);
      addBitmap2DiskCache(bitmapKey, bitmap);
    }
  }

  private void addBitmap2MemCache(String bitmapKey, Bitmap bitmap) {
    if (null != mMemcache) {
      if (mMemcache.get(bitmapKey) == null) {
        mMemcache.put(bitmapKey, bitmap);
      }
    }
  }

  private void addBitmap2DiskCache(String bitmapKey, Bitmap bitmap) {
    if (null != mDiskCache) {
      // 暂时直接调用SimpleDiskCache里的方法,
      //不同点再于，这里存储文件需要使用hash
      
      mDiskCache.addBitmap2Cache(hashKeyForDisk(bitmapKey), bitmap);
    }
  }

  /**
   * @author zhangyw
   * @param bitmapKey
   * @return could be null,if no match or no cache
   */
  public Bitmap getBitmapFromMemCache(String bitmapKey) {
    if (null != mMemcache) {
      return mMemcache.get(bitmapKey);
    }
    return null;
  }

  /**
   * @author zhangyw
   * @param bitmapKey
   * @return could be null,if no match or no cache
   */
  public Bitmap getBitmapFromDiskCache(String bitmapKey) {
    if(null!=mDiskCache){
      // 暂时直接调用SimpleDiskCache里的方法,
      //不同点再于，这里存储文件需要使用hash
      return mDiskCache.readBitmapFromCache(hashKeyForDisk(bitmapKey));
    }
    return null;
  }

  /**
   * A hashing method that changes a string (like a URL) into a hash suitable
   * for using as a disk filename.
   */
  public static String hashKeyForDisk(String key) {
    String cacheKey;
    try {
      final MessageDigest mDigest = MessageDigest.getInstance("MD5");
      mDigest.update(key.getBytes());
      cacheKey = bytesToHexString(mDigest.digest());
    } catch (NoSuchAlgorithmException e) {
      cacheKey = String.valueOf(key.hashCode());
    }
    return cacheKey;
  }

  /**
   * Get a usable cache directory (external if available, internal otherwise).
   * 
   * @param context
   *          The context to use
   * @param uniqueName
   *          A unique directory name to append to the cache dir ,null means
   *          nothing appended
   * @return The cache dir
   */
  public static File getDiskCacheDir(Context context, String uniqueName) {
    if(null!=context){
      return IOUtils.getPrivateCacheDir(context, uniqueName);
    }
    return null;
  }

  private static String bytesToHexString(byte[] bytes) {
    // http://stackoverflow.com/questions/332079
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
      String hex = Integer.toHexString(0xFF & bytes[i]);
      if (hex.length() == 1) {
        sb.append('0');
      }
      sb.append(hex);
    }
    return sb.toString();
  }
}
